package com.luo.d3s.ext.data.permission.dto.parse;

import com.luo.d3s.ext.data.permission.anno.DataPermission;
import com.luo.d3s.ext.data.permission.dto.context.base.BaseDpDto;
import com.luo.d3s.ext.data.permission.dto.context.base.BaseUserDto;
import com.luo.d3s.ext.data.permission.util.DpUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * SQl解析参数DTO
 *
 * @author luohq
 * @date 2023-06-20 14:23
 */
public class SqlParseParamDto {

    public static String DEFAULT_INVALID_ID = "0";
    public static final String EMPTY = "";
    public static final String SEPARATOR = ",";
    public static final String SINGLE_QUOTE = "'";
    public static final String DOUBLE_QUOTE = "\"";

    private String deptAlias;
    private String deptIdColumn;
    private String deptId;
    private Collection<String> deptAndChildDeptIds;

    private String userAlias;
    private String userIdColumn;
    private String userId;
    private Collection<String> manageDeptIds;
    private Collection<String> manageUserIds;

    private Map<String, String> paramMap;

    public SqlParseParamDto() {
    }

    public SqlParseParamDto(DataPermission dpAnno, BaseUserDto user, BaseDpDto dp) {
        this.deptAlias = DpUtils.withNullDefault(dpAnno.deptAlias(), EMPTY);
        this.deptIdColumn = DpUtils.withNullDefault(dpAnno.deptIdColumn(), EMPTY);
        this.userAlias = DpUtils.withNullDefault(dpAnno.userAlias(), EMPTY);
        this.userIdColumn = DpUtils.withNullDefault(dpAnno.userIdColumn(), EMPTY);
        this.userId = DpUtils.withNullDefault(user.getUserId(), DEFAULT_INVALID_ID);
        this.deptId = DpUtils.withNullDefault(user.getDeptId(), DEFAULT_INVALID_ID);
        this.deptAndChildDeptIds = user.getDeptAndChildDeptIds();
        this.manageDeptIds = dp.getManageDeptIds();
        this.manageUserIds = dp.getManageUserIds();

        //初始参数Map
        this.initParamMap(user);
    }

    public static SqlParseParamDto of(DataPermission dpAnno, BaseUserDto user, BaseDpDto dp) {
        return new SqlParseParamDto(dpAnno, user, dp);
    }

    private void initParamMap(BaseUserDto user) {
        this.paramMap = new HashMap<>(8);

        //基础参数
        paramMap.put("deptAlias", this.deptAlias);
        paramMap.put("deptIdColumn", this.deptIdColumn);
        paramMap.put("userAlias", this.userAlias);
        paramMap.put("userIdColumn", this.userIdColumn);
        paramMap.put("userId", this.userId);
        paramMap.put("deptId", this.deptId);
        paramMap.put("deptAndChildDeptIds", this.idsToString(this.deptAndChildDeptIds, EMPTY));
        paramMap.put("userIdIdWithSingleQuote", this.idsToString(Collections.singleton(this.userId), SINGLE_QUOTE));
        paramMap.put("deptIdWithSingleQuote", this.idsToString(Collections.singleton(this.deptId), SINGLE_QUOTE));
        paramMap.put("deptAndChildDeptIdsWithSingleQuote", this.idsToString(this.deptAndChildDeptIds, SINGLE_QUOTE));
        paramMap.put("userIdIdWithDoubleQuote", this.idsToString(Collections.singleton(this.userId), DOUBLE_QUOTE));
        paramMap.put("deptIdWithDoubleQuote", this.idsToString(Collections.singleton(this.deptId), DOUBLE_QUOTE));
        paramMap.put("deptAndChildDeptIdsWithDoubleQuote", this.idsToString(this.deptAndChildDeptIds, DOUBLE_QUOTE));
        paramMap.put("manageDeptIds", this.idsToString(this.manageDeptIds, EMPTY));
        paramMap.put("manageUserIds", this.idsToString(this.manageDeptIds, EMPTY));
        paramMap.put("manageDeptIdsWithSingleQuote", this.idsToString(this.manageDeptIds, SINGLE_QUOTE));
        paramMap.put("manageUserIdsWithSingleQuote", this.idsToString(this.manageUserIds, SINGLE_QUOTE));
        paramMap.put("manageDeptIdsWithDoubleQuote", this.idsToString(this.manageDeptIds, DOUBLE_QUOTE));
        paramMap.put("manageUserIdsWithDoubleQuote", this.idsToString(this.manageUserIds, DOUBLE_QUOTE));

        //附加参数
        if (null != user.getAdditionalParams() && !user.getAdditionalParams().isEmpty()) {
            this.paramMap.putAll(user.getAdditionalParams());
        }
    }


    /**
     * 填充sql中的参数
     *
     * @param sql sql
     * @return 填充后的sql
     */
    public String fillSqlParams(String sql) {
        Set<Map.Entry<String, String>> entries = this.paramMap.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String pName = entry.getKey();
            String pValue = entry.getValue();
            String placeHolder = String.format("{%s}", pName);
            //特殊处理table别名参数（带有Alias后缀）
            if (this.isAliasParam(pName)) {
                placeHolder = String.format("{%s}.", pName);
                pValue = (null == pValue || pValue.isEmpty()) ? "" : pValue.concat(".");
            }
            sql = sql.replace(placeHolder, pValue);
        }
        return sql;
    }

    public String getDeptAlias() {
        return deptAlias;
    }

    public String getDeptIdColumn() {
        return deptIdColumn;
    }

    public String getDeptId() {
        return deptId;
    }

    public String getUserAlias() {
        return userAlias;
    }

    public String getUserIdColumn() {
        return userIdColumn;
    }

    public String getUserId() {
        return userId;
    }

    public Collection<String> getManageDeptIds() {
        return manageDeptIds;
    }

    public Collection<String> getManageUserIds() {
        return manageUserIds;
    }

    public Map<String, String> getParamMap() {
        return paramMap;
    }

    /**
     * 是否是table别名参数（带有Alias后缀）
     *
     * @param paramName 参数名
     * @return 是否为别名参数
     */
    private Boolean isAliasParam(String paramName) {
        return paramName.endsWith("Alias");
    }

    /**
     * ID列表转换为半角逗号分隔的字符串
     *
     * @param ids   ID列表
     * @param quote 每个ID使用指定的符号进行包围（例如空、单引号、双引号等）
     * @return 半角逗号分隔的ID字符串
     */
    private String idsToString(Collection<String> ids, String quote) {
        if (null == ids || ids.isEmpty()) {
            return DEFAULT_INVALID_ID;
        }

        Set<String> idStrSet = ids.stream()
                .map(id -> quote.concat(id.concat(quote)))
                .collect(Collectors.toSet());

        return idStrSet.stream().collect(Collectors.joining(SEPARATOR));
    }

    @Override
    public String toString() {
        return this.paramMap.toString();
    }
}
